#!/usr/bin/env python
# _*_ coding: utf-8 _*_
# @Time : ${DATE} ${TIME} 
# @Author : fatOrange 
# @Version：alpha
# @File : ${NAME}.py
# @desc :c++头文件分析工具

import os
import re
from pathlib import Path
import sys,getopt
import networkx as nx # graph structure 
Folder_path = r"F:/Codes/sugon_code/ROCclr"
Folder_path = os.path.abspath(Folder_path)
isFolder = False
# isFolder = True
file_suffix = [".cpp",".hpp",".h"]
sys_parm = ''
files_ = []
all_file_path  = []
file_pathes = []
dirs_ = []
p= re.compile('.+?"(.+?)"')# re parttern 双引号之间的值
pp =  re.compile('[^/]+(?!.*/)')# 斜杠后面的值

def parsePara(argv):
    global Folder_path
    global isFolder
    global sys_parm 
    try:
      opts, args = getopt.getopt(argv,"P:p:sf")
    except getopt.GetoptError:
        print('hierarchify_Project.py [-p <folder path> ,-P <project path> ,-s : simple infomation mode ,-f : full information mode]')
        sys.exit(2)
    hasPath = False
    for opt, arg in opts:
        if opt == '-h':
            print('hierarchify_Project.py [-p <folder path>  ,-P <project path> ,-s : simple infomation mode ,-f : full information mode]')
            sys.exit()
        elif opt in ("-s"):
            pass
        elif opt in ("-f"):
            
            sys_parm = 'f'
        elif opt in ("-p"):
            
            hasPath = True
            isFolder = True
            Folder_path = os.path.abspath(arg)
        elif opt in ("-P"):
            hasPath = True
            Folder_path = os.path.abspath(arg)
    if hasPath == False :
        print("please specificate path please add -h for help")
        exit(0)


def walkFile(file):
    for root, dirs, files in os.walk(file):
        # root 表示当前正在访问的文件夹路径
        # dirs 表示该文件夹下的子目录名list
        # files 表示该文件夹下的文件list
        # 遍历文件
        for f in files:
            formatted = os.path.splitext(f)
            if len(formatted)>1:
                suffix = formatted[1]
                if suffix not in file_suffix:
                    continue
            
            # files_.append(f)
            file_pathes.append(root)
            all_file_path.append(os.path.join(root, str(f)))
            global files_ 
            if isFolder:
                files_.append(f)
            else:
                files_ = all_file_path.copy()
            # print(os.path.join(root, f))
            # print(root,":",f)
        # 遍历所有的文件夹
        for d in dirs:
            dirs_.append(d)
            # print(os.path.join(root, d))
# print(dirs_)

def generateHeader_dict():
    header_dict = {}
    for i in range(len(all_file_path)):
        path = all_file_path[i]
        # print(path)
        fo = open(path,'r', encoding='UTF-8')
        key_ = files_[i]
        value_ = []
        key_path = [] 
        try:
            while True:
                text_line = fo.readline()
                if text_line:
                    if text_line.find("#include ") == 0:
                        p1 = p.findall(text_line)
                        # print(path,p1)
                        # print(type(p1),len(p1))
                        # 
                        if(len(p1)>0):
                            # p2 = pp.findall(p1[0])[0]
                            p1 = p1[0]
                            
                            # print(p1,type(p1))
                            if isFolder:
                                p1 = pp.findall(p1)[0]
                                value_.append(p1)
                            else:
                                p1 = p1.replace('/','\\')
                                vise_path  = os.path.join(file_pathes[i],p1)
                                main_path = os.path.join(Folder_path,p1)
                                # print(main_path)
                                # print(vise_path)
                                v_path = Path(vise_path)
                                m_path = Path(main_path)
                                if m_path.is_file():
                                    value_.append(main_path)
                                elif v_path.is_file():
                                    value_.append(vise_path)
                else:
                    break
        finally:
                fo.close()
        
        key_path.append(file_pathes[i])
        if isFolder:
            header_dict.update({key_:value_})
        else:
            header_dict.update({os.path.abspath(key_):value_})
    return header_dict


def hierarchify(header_dict):
    Level = []
    G = nx.DiGraph(incoming_graph_data=header_dict)
    topLevel = []
    bottomLevel = []
    middleLevel = []
    g = G.copy()
    for n in g.nodes():#扫一圈把不存在的都扫了
        if n not in files_:
            G.remove_node(n)

    while(len(G.nodes())>0):
        degree = G.degree()
        inDegree = G.in_degree()
        outDegree = G.out_degree() 
        least_degree   = 999
        least_inDegree = 999
        least_outDegree= 999
        for n in G.nodes():
            if(degree[n]<least_degree):
                least_degree = degree[n]
            if(inDegree[n]<least_inDegree):
                least_inDegree = inDegree[n]
            if(outDegree[n]< least_outDegree):
                least_outDegree = outDegree[n]
        g = G.copy()
        newTopLevel = []
        newBottomLevel = []
        newLoopLevel    = []
        newMiddleLevel = []
        hasLoopFlag = True

        for n in g.nodes():#扫一圈
            if(outDegree[n]==0): #出度为0 说明是根节点 不依赖任何文件 目前处于最顶层
                newTopLevel.append(n)
                G.remove_node(n)
                hasLoopFlag = False
                #执行到这里说明既有出度也有入度且不可分离，只要把剩下的放在level中就行了
        if hasLoopFlag:
            Gb = G.copy()# 保存环境
            inDegree = Gb.in_degree()
            while(len(Gb.nodes())>0):
                gb = Gb.copy()
                hasLoopFlag = True
                for n in gb.nodes():
                    if(inDegree[n]== 0 ):# 没有入度
                        Gb.remove_node(n)
                        hasLoopFlag = False
                if hasLoopFlag:
                    for n in gb.nodes():
                        if n in files_:
                            newLoopLevel.append(n)
                        Gb.remove_node(n)
                        G.remove_node(n)
            newTopLevel = newLoopLevel
        Level.append(newTopLevel)
    # print(Level)
    return Level

'''
通过图论中的算法解决loop relationship的问题
算法如下
1将dict抽象成图
2 计算各个节点的度
3 将每个节点的度减1
4 是否都为0 如果为0则之前的节点是一个循环图，如果不是0则跳转到3
'''
def findLoopRelationship(header_dict,):
    G = nx.DiGraph(incoming_graph_data=header_dict)
    # G.add
    leaset_degree = 1
    while(len(G.nodes)>0):
        
        add_flag = True
        degree = G.degree()
        g = G.copy()
        for n in g.nodes():# 去掉无用的节点
            if degree[n] == leaset_degree:
                G.remove_node(n)
                add_flag = False
        if add_flag:
            leaset_degree+=1
        g = G.copy()
        for i in g.nodes():
            for j in g.nodes():
                if degree[i]!=degree[j]:
                    continue
        if(len(G.nodes)>0):
            return  list(G.nodes)

    pass

parsePara(sys.argv[1:])
folder = Path(Folder_path)
assert(folder.exists())
walkFile(folder)
# print("files_",files_)

header_dict = generateHeader_dict()
# print(header_dict)
hrk = hierarchify(header_dict)

# print(hrk)

if sys_parm == 'f':
    print("此文件夹有这些文件:")
    print(files_)
    for i in range(len(hrk)):
        level = hrk[i]
        print("第",i+1,"层有:")
        for key in level:
            print(key,",它引用的include有:",header_dict[key])
else:
    for i in range(len(hrk)):
        level = hrk[i]
        print("第",i+1,"层有:")
        for key in level:
            print(key)

